package model.tests;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.Collection;

import model.collisions.CollisionCategory;
import model.collisions.Collisionable;
import model.elements.Bomb;
import model.elements.BombModifier;
import model.elements.NonPlayerCharacter;
import model.elements.PlayerCharacter;
import model.elements.SpeedModifier;
import model.elements.TTLModifier;
import model.elements.Wall;
import model.elements.ArtificialIntelligence.AlwaysGoRight;
import model.elements.attributes.Coordinate2D;
import model.elements.attributes.Damage;
import model.elements.attributes.Lives;
import model.elements.attributes.Speed;
import model.elements.commands.ElementCommand;
import model.elements.commands.ElementCommandFactory;
import model.elements.configurations.NonPlayerCharacterConfig;
import model.elements.states.movement.MovingDown;
import model.elements.states.movement.MovingLeft;
import model.elements.states.movement.MovingRight;
import model.elements.states.movement.MovingUp;
import model.elements.states.weapon.BombEquipped;
import model.exceptions.ModelExceptions;
import model.map.ElementsMap;

import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

// TODO: Auto-generated Javadoc
/**
 * The Class PlayerCharacterTest.
 */
public class PlayerCharacterTest
{
	
	/** The test map. */
	private static ElementsMap		testMap		 		= new ElementsMap(20,20);
	
	/** The test player character. */
	private PlayerCharacter 	 	testPlayerCharacter;
	
	/**
	 * The Class NPC_TEST_MOVE_RIGHT.
	 */
	public class NPC_TEST_MOVE_RIGHT extends NonPlayerCharacterConfig
	{
		
		/**
		 * Instantiates a new npc test move right.
		 */
		public NPC_TEST_MOVE_RIGHT(  )
		{
			m_lives 		 = new Lives(5);
			m_speed 		 = new Speed(1);
			m_equippedWeapon = new BombEquipped(3);
			m_IA 			 = new AlwaysGoRight(  );
			m_damage 		 = new Damage( 5 );
			m_category 		 = CollisionCategory.C_NPC;
		}
	}
	
	/**
	 * Sets the up before class.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@BeforeClass
	public static void setUpBeforeClass() throws ModelExceptions
	{	
		ElementCommandFactory.setup(testMap);
	}
	
	/**
	 * Sets the up.
	 *
	 * @throws Exception the exception
	 */
	@Before
	public void setUp() throws Exception
	{
		testPlayerCharacter = new PlayerCharacter(  new Coordinate2D( 0,0 )  );
		testMap.addElement( testPlayerCharacter );
	}

	/**
	 * Tear down.
	 *
	 * @throws Exception the exception
	 */
	@After
	public void tearDown() throws Exception
	{
		testMap.clear();
	}

	/*
	 // DEPRECATED

	@Test
	public void testIsPlayerCharacter()
	{
		assertTrue( testPlayerCharacter.isPlayerCharacter() ); 
	}
	*/
	
    /*
    // DEPRECATED
	@Test
	public void testIsNonPlayerCharacter() {
		assertFalse( testPlayerCharacter.isNonPlayerCharacter() ); 
	}
    */
	
	/*
	 // DEPRECATED
	@Test
	public void testUniqueId() throws ModelExceptions {
		NonPlayerCharacter 	elemento1 = new NonPlayerCharacter( new Coordinate2D(1,0), new NPC_TEST_MOVE_RIGHT(  ) );
		PlayerCharacter 	elemento2 = new PlayerCharacter( new Coordinate2D(0,0) );	
		int element1Id 				  = elemento1.getId();	
		int element2Id 				  = elemento2.getId();
		assertTrue( element1Id != element2Id );		
	}
    */
	
	/**
	 * Test get position.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testGetPosition() throws ModelExceptions {
		
		Coordinate2D newPosition = new Coordinate2D(1,1);
		
		testPlayerCharacter.setPosition( newPosition );
		
		assertTrue( testPlayerCharacter.getPosition().equals(newPosition) );
		
	}
	
	/**
	 * Test move to open space.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testMoveToOpenSpace() throws ModelExceptions {
		
		ElementCommand testPlayerCommand = ElementCommandFactory.getElementMove( new MovingRight() );
		
		Coordinate2D initialPosition = testPlayerCharacter.getPosition();
		
		testPlayerCharacter.addCommand( testPlayerCommand );
		
		testPlayerCharacter.update();
		
		Coordinate2D finalPosition = testPlayerCharacter.getPosition();
		
		assertTrue( initialPosition.equals( Coordinate2D.minus( finalPosition, new  Coordinate2D(1,0) ) ) ); 
		
	}
	
	/**
	 * Test move to open out of bouds.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testMoveToOpenOutOfBouds() throws ModelExceptions {
		
		ElementCommand testPlayerCommand = ElementCommandFactory.getElementMove( new MovingLeft() );
		
		testPlayerCharacter.setPosition( new Coordinate2D(0,0) );
		
		testPlayerCharacter.addCommand( testPlayerCommand );
		
		testPlayerCharacter.update();
		
		Coordinate2D finalPosition = testPlayerCharacter.getPosition();
		
		assertTrue( finalPosition.equals( new  Coordinate2D(0,0) ) ); 
		
	}
	
	/**
	 * Test move to wall.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testMoveToWall() throws ModelExceptions {
		
		ElementCommand testPlayerCommand = ElementCommandFactory.getElementMove( new MovingDown() );
		
		Coordinate2D initialPosition =  new Coordinate2D(0,1);
		
		testPlayerCharacter.setPosition( initialPosition );
		
		testMap.addElement( new Wall( new Coordinate2D(0,0) ) );
		
		testPlayerCharacter.addCommand( testPlayerCommand );
		
		testPlayerCharacter.update();
		
		Coordinate2D finalPosition = testPlayerCharacter.getPosition();
		
		assertTrue( finalPosition.equals(initialPosition) ); 
		
	}
	
	/**
	 * Test drop bomb.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testDropBomb() throws ModelExceptions {
		
		ElementCommand testPlayerCommand = ElementCommandFactory.getElementUseWeapon();
		
		Collection<Collisionable> testList = testMap.getElementsIn( testPlayerCharacter.getPosition() );
		
		int initialCount = testList.size();
		
		testPlayerCharacter.addCommand( testPlayerCommand );
		
		testPlayerCharacter.update();
		
		testList = testMap.getElementsIn(testPlayerCharacter.getPosition());
		
		int finalCount = testList.size();
		
		assertTrue( finalCount == ( initialCount + 1 ) ); 
		
	}
	
	/**
	 * Test move to npc.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testMoveToNPC() throws ModelExceptions {
		
		ElementCommand testPlayerCommand = ElementCommandFactory.getElementMove( new MovingUp() );
		
		testMap.addElement( new NonPlayerCharacter( new Coordinate2D(0,1), new NPC_TEST_MOVE_RIGHT(  ) ) );
		
		testPlayerCharacter.addCommand( testPlayerCommand );
		
		testPlayerCharacter.setPosition( new Coordinate2D(0,0) );
		
		testPlayerCharacter.update();
		
		assertTrue( testPlayerCharacter.getLives().getValue() == 0 );
		
		assertFalse( testPlayerCharacter.isAlive() );
		
	}
	
	/**
	 * Test move to dead npc.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testMoveToDeadNPC() throws ModelExceptions {
		
		ElementCommand testPlayerCommand = ElementCommandFactory.getElementMove( new MovingRight() );
		
		Coordinate2D initialPosition = testPlayerCharacter.getPosition();
		
		NonPlayerCharacter testNonPlayerCharacter = new NonPlayerCharacter( Coordinate2D.add( initialPosition, new Coordinate2D(1,0) ), new NPC_TEST_MOVE_RIGHT(  ) );
		
		testMap.addElement( testNonPlayerCharacter );
		
		testNonPlayerCharacter.takeDamage( new Damage(40000) );
		
		assertFalse( testNonPlayerCharacter.isAlive() );
		
		testPlayerCharacter.addCommand( testPlayerCommand );		
		
		testPlayerCharacter.update();
		
		Coordinate2D finalPosition = testPlayerCharacter.getPosition();
		
		assertTrue( testPlayerCharacter.getLives().getValue() == 5 );
		
		assertTrue( finalPosition.equals( new Coordinate2D(1,0) ) ); 
		
		assertTrue( testPlayerCharacter.isAlive() );
		
	}
	
	/**
	 * Test move to speed upgrade.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testMoveToSpeedUpgrade() throws ModelExceptions {
		
		ElementCommand testPlayerCommand = ElementCommandFactory.getElementMove( new MovingRight() );;
		
		Coordinate2D initialPosition = testPlayerCharacter.getPosition();
		
		SpeedModifier testSpeedModifier = new SpeedModifier( Coordinate2D.add( initialPosition, new Coordinate2D(1,0) ), 2.0 );
		
		testMap.addElement( testSpeedModifier );
		
		double initialSpeed = testPlayerCharacter.getSpeed().getValue();
		
		testPlayerCharacter.addCommand( testPlayerCommand );
		
		testPlayerCharacter.update();
		
		assertTrue( Math.abs(testPlayerCharacter.getSpeed().getValue() - ( initialSpeed * 2.0 )) < .0000001 );
		
		assertFalse( testSpeedModifier.isAlive() );
	
	}
	
	/**
	 * Test move to ttl upgrade.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testMoveToTTLUpgrade() throws ModelExceptions {
		
		ElementCommand testPlayerCommand = ElementCommandFactory.getElementMove( new MovingRight() );
		
		Coordinate2D initialPosition = testPlayerCharacter.getPosition();
		
		testPlayerCharacter.addCommand( ElementCommandFactory.getElementUseWeapon() );
		
		testPlayerCharacter.update();
		
		Bomb testBomb1 = null;		
		for ( Object element : testMap.getElementsIn( initialPosition ).toArray() )
			if( element.getClass() == Bomb.class )
				testBomb1 = (Bomb)element ;
		
		TTLModifier testTTLModifier = new TTLModifier( Coordinate2D.add( initialPosition, new Coordinate2D(1,0) ), 2.0 );
		
		testMap.addElement( testTTLModifier );
		
		testPlayerCharacter.addCommand( testPlayerCommand );
		
		testPlayerCharacter.update();
		
		assertFalse( testTTLModifier.isAlive() );
		
		testPlayerCharacter.addCommand( testPlayerCommand );
		
		testPlayerCharacter.update();
		
		testPlayerCharacter.addCommand( ElementCommandFactory.getElementUseWeapon() );
		
		testPlayerCharacter.update();
		
		Bomb testBomb2 = null;		
		for ( Object element : testMap.getElementsIn( testPlayerCharacter.getPosition() ).toArray() )
			if( element.getClass() == Bomb.class )
				testBomb2 = (Bomb)element ;
		
		int ttl1 = testBomb1.getTTL().getValue();
		int ttl2 = testBomb2.getTTL().getValue();
		
		assertTrue( ttl1*2 == ttl2 );
		
	}
	
	/**
	 * Test move to Bomb upgrade.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testMoveToBombUpgrade() throws ModelExceptions {
		
		ElementCommand testPlayerCommand = ElementCommandFactory.getElementMove( new MovingRight() );
		
		Coordinate2D initialPosition = testPlayerCharacter.getPosition();
		
		testPlayerCharacter.addCommand( ElementCommandFactory.getElementUseWeapon() );
		
		testPlayerCharacter.update();
		
		Bomb testBomb1 = null;		
		for ( Object element : testMap.getElementsIn( initialPosition ).toArray() )
			if( element.getClass() == Bomb.class )
				testBomb1 = (Bomb)element ;
		
		BombModifier testBombModifier = new BombModifier( Coordinate2D.add( initialPosition, new Coordinate2D(1,0) ) );
		
		testMap.addElement( testBombModifier );
		
		testPlayerCharacter.addCommand( testPlayerCommand );
		
		testPlayerCharacter.update();
		
		assertFalse( testBombModifier.isAlive() );
		
		testPlayerCharacter.addCommand( testPlayerCommand );
		
		testPlayerCharacter.update();
		
		testPlayerCharacter.addCommand( ElementCommandFactory.getElementUseWeapon() );
		
		testPlayerCharacter.update();
		
		Bomb testBomb2 = null;		
		for ( Object element : testMap.getElementsIn( testPlayerCharacter.getPosition() ).toArray() )
			if( element.getClass() == Bomb.class )
				testBomb2 = (Bomb)element ;
		
		int blastRange1 = testBomb1.getBlastRange();
		int blastRange2 = testBomb2.getBlastRange();
		
		assertTrue( blastRange1 < blastRange2 );
		
		CollisionCategory blastType1 = testBomb1.getBlastType();
		CollisionCategory blastType2 = testBomb2.getBlastType();
		
		assertTrue( blastType1 != blastType2 );
		
	}


}
